package sis.db;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2019-07-30 09:34
 * @notify
 * @version 1.0
 */


import sis.util.IOUtil;

import java.io.*;
import java.util.RandomAccess;

public class DataFile {

    //db文件类型
    public static final String DATA_EXT = ".db";
    //索引文件类型
    public static final String KEY_EXT = ".idx";

    //db文件名
    private String dataFilename;
    //索引文件名
    private String keyFilename;

    //读写流
    private RandomAccessFile db;
    //索引类
    private KeyFile keys;

    //构造子
    public static DataFile create(String filebase) throws IOException {
        return new DataFile(filebase, true);
    }

    //构造子,指定是否删除之前的文件缓存
    private DataFile(String filebase, boolean deleteFiles) throws IOException {
        //拼接文件名
        dataFilename = filebase + DATA_EXT;
        keyFilename = filebase + KEY_EXT;
        //如果删除缓存,则删除
        if (deleteFiles) {
            deleteFiles();
        }
        //创建文件
        openFiles();
    }

    //创建文件
    public static DataFile open(String filebase) throws IOException {
        return new DataFile(filebase, false);
    }

    //添加数据
    public void add(String key, Object object) throws IOException {
        //当前存储文件长度
        long position = db.length();
        //将对象写入字节数组
        byte[] bytes = getBytes(object);
        //设置写入 开始位置
        db.seek(position);
        //写入文件
        db.write(bytes, 0, bytes.length);
        //添加索引
        keys.add(key, position, bytes.length);
    }

    //通过索引id查询文件
    public Object findBy(String id) throws IOException {
        //如果索引中没有该id则返回null
        if (!keys.containsKey(id)) {
            return null;
        }
        //查询 存储位置
        long position = keys.getPosition(id);
        //设置起始位置
        db.seek(position);
        //查询文件长度
        int length = keys.getLength(id);
        //查询
        return read(length);
    }

    //返回索引长度
    public int size() {
        return keys.size();
    }

    //关闭
    public void close() throws IOException {
        keys.close();
        db.close();
    }

    //删除
    public void deleteFiles() {
        IOUtil.delete(dataFilename, keyFilename);
    }

    //给定数据长度
    private Object read(int length) throws IOException {
        //创建 存储数据的数组
        byte[] bytes = new byte[length];
        //从此文件读取 b.length字节到字节数组，从当前文件指针开始。
        db.readFully(bytes);
        //返回对象
        return readObject(bytes);
    }

    //将字节数组转换对象
    private Object readObject(byte[] bytes) throws IOException {
        //输出流
        ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(bytes));
        try {
            try {
                //读取数据
                return input.readObject();
            } catch (ClassNotFoundException unlikely) {
                return null;
            }
        } finally {
            //关闭流
            input.close();
        }
    }

    //创建文件
    private void openFiles() throws IOException {
        //创建 索引文件
        keys = new KeyFile(keyFilename);
        /*
         * 值的意思是“r”只供阅读。调用结果对象的任何写方法都会引发IOException。
         * “rw”用于阅读和写作。如果文件不存在，则尝试创建它。
         * 与“rw”一样，“rws”可用于读写， 并要求对文件内容或元数据的每次更新都要同步写入底层存储设备。
         * 与“rw”一样，“rwd”用于读写，并要求对文件内容的每次更新都要同步写入底层存储设备。
         * */
        //创建一个可读写的 数据文件
        db = new RandomAccessFile(new File(dataFilename), "rw");
    }

    //将对象写入字节数组
    private byte[] getBytes(Object object) throws IOException {
        //字节写入流
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        //对象写入流
        ObjectOutputStream outputStream = new ObjectOutputStream(byteStream);
        //写入对象到数组
        outputStream.writeObject(object);
        //刷新
        outputStream.flush();
        //返回字节数组
        return byteStream.toByteArray();
    }
}
